# JSX核心基础、条件与列表渲染、事件与样式绑定
# 一、JSX简介
# 1、JSX是什么
JSX 是 JavaScript 语法扩展,可以让你在 JavaScript 文件中书写类似 HTML 的标签。
// 以下为一段 JS 代码
function App() {
// 函数的返回值就是一个JSX
return <div title="标题">Hello React !!</div>
}
export default App
# 2、Babel 在线工具转换 JSX
浏览器默认无法识别JSX,因此在 React 项目内部借助了 Babel 编译器将 JSX 代码转换为了浏览器可以理解的函数调用。
打开 Babel 在线转换器 (opens new window),将下面JSX 转换成对应的函数

- 转换前的 jsx 代码
function App() {
return <div title="标题">Hello React !!</div>
}
export default App
- babel 转换后代码
import { jsx as _jsx } from "react/jsx-runtime";
// App.tsx 根组件
function App() {
/* jsx 被转换成了一个 _jsx函数 (React 内置了这个函数),
第一个参数为 html标签名
第二个参数是一个对象,用来设置html标签属性和子元素内容
title 为标签属性
children 属性值为子元素内容
_jsx 函数调用后会返回 DOM <div title="标题">Hello React !!</div>
*/
return /*#__PURE__*/_jsx("div", {
title: "\u6807\u9898",
children: "Hello React !!"
});
}
export default App;
# 3、React 中 JSX 转换分析
你可以执行 npm create vite@latest 命令,创建一个 React 项目,然后修改 App.jsx 文件 内容如下
// App.tsx 根组件
function App() {
return <div title="标题">Hello React !!</div>
}
export default App;
执行 npm run dev 运行项目后,在浏览器的控制台,可看到 App 组件中的 JSX 被转换成了一个 函数调用

# 二、扩展内容:选学
# 3、手动配置 Babel 转换 JSX
本小节,我们通过手动配置Babel,用 Babel 来转换 JSX,看转换后是否是我们上面提到的效果。
演示步骤
- 创建项目一个 文件夹
jsx,然后在当前目录下执行npm init初始化package.json
npm init # 初始化package.json
- 安装Babel转换JSX所需要的三个包
- @babel/core babel的核心包
- @babel/cli babel命令行工具
- @babel/preset-react babel预设包,专门用于React应用程序,将React的JSX代码转换为纯JavaScript代码的过程中,确保代码的兼容性和正确性
npm install --save-dev @babel/core @babel/preset-react @babel/cli
- 在
babel.config.js文件中来配置 Babel,告诉它使用@babel/preset-react来转换JSX
module.exports = {
presets: ['@babel/preset-react']
};
- 创建
App.jsx文件,文件内容如下
function App() {
// 函数的返回值就是一个JSX
return <div title="标题">Hello React !!</div>
}
export default App
- 执行以下命令,Babel 将采用 React 预设来转换 JSX,然后将转换后的结果输出的
App.js文件
npx babel App.jsx --out-file App.js
最终在当前目录下生成了 App.js,内容如下:
function App() {
// 函数的返回值就是一个JSX
return /*#__PURE__*/React.createElement("div", {
title: "\u6807\u9898" // 标题
}, "Hello React !!");
}
export default App;
# 3、JSX 与 React的关系
通过上面转换后的代码,我们可以看到 JSX 在这里与 React 是强绑定的(转换后返回的是 React.createElement) 函数。
在最新的 React17 之后,JSX 和 React 是相互独立的,但它们经常一起使用。不过你可以单独使用其中一个。JSX 是一种语法扩展,而 React 则是一个 JavaScript 的库。
代码演示
- 将上面代码中的
babel.config.js中的配置修改成如下:
| runtime 值 | 说明 |
|---|---|
| automatic | 会自动导入 JSX 编译后所需的函数 |
| classic | 不会自动导入任何内容 |
注意:在 Babel 8 中,默认运行时将切换为 automatic。我们现在演示的是 Babel 7 版本,所以需要单独配置 runtime 选项值
// babel.config.js
module.exports = {
presets: [
['@babel/preset-react',{
"runtime": "automatic"
}]
]
};
- 然后重新执行以下命令来转换JSX
npx babel App.jsx --out-file App.js
- 最终转换后生成的
App.js文件内容如下
// 由编译器插入(不需要自己导入)
import { jsx as _jsx } from "react/jsx-runtime";
function App() {
// 函数的返回值就是一个JSX
return /*#__PURE__*/_jsx("div", {
title: "\u6807\u9898",
children: "Hello React !!"
});
}
export default App;
通过编译后的代码,我们可以看到此时生成的 JS 代码中,并没有用到 React 这个对象。也就是说,你可以在任何地方使用 Babel 来转换 JSX(但仍记得要安装react,因为需要用到 react/jsx-runtime 这个文件)
# 三、JSX 书写规则
JSX 是 JavaScript 的语法扩展,它允许你在 JavaScript 文件中写类似 HTML 的标记。虽然 JSX 看起来很像 HTML,但在实际编写时有自己的一套规则。掌握这些规则,可以避免常见的错误,让代码更规范、更易维护。
以下是 JSX 书写时必须遵守的 10 条核心规则。
- 只能返回一个根元素
- 标签必须正确闭合
- 属性命名大部分使用驼峰式命名法
- 了解特殊属性名的处理
- JSX 中注释书写规范
- JSX 中通过大括号使用 JS
- 了解 哪些值不会被渲染到页面
- JSX 的哪些地方可以使用大括号
- 变量中保存 JSX
# 1、只能返回一个根元素
- 如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来。
function App(){
return <div>
<h3>只能返回一个根元素</h3>
<div>如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来</div>
</div>
}
export default App
- 如果不想多添加一层嵌套,可以使用
<><>将标签包裹起来
function App(){
return <>
<h3>只能返回一个根元素</h3>
<div>如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来</div>
</>
}
export default App
- 你可以把
<><>看作是<Fragment></Fragment>内置标签的简写形式,在某些特殊情况下不能采用简写,只能采用<Fragment></Fragment>写法。(往后会讲)
// Fragment是React提供的内置标签,记得一定要导入
import { Fragment } from "react"
function App(){
return <Fragment>
<h3>只能返回一个根元素</h3>
<div>如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来</div>
</Fragment>
}
export default App
- 默认情况下,紧跟在 return 后面的标签不能换行,如果想要换行,可以使用
()小括号包裹标签。
为了代码可读性,建议使用小括号包裹 JSX
import { Fragment } from "react"
function App(){
return (
<>
<h3>只能返回一个根元素</h3>
<div>如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来</div>
</>
)
}
export default App
# 2、标签必须正确闭合
- JSX 要求标签必须正确闭合。
- 像
<img>这样的自闭合标签必须书写成<img /> - 像
<div>这样成对出现的标签,必须写成<div> xxx </div>
function App(){
return (
<>
<img src="/vite.svg" />
<div>标签必须闭合</div>
</>
)
}
export default App
# 3、属性命名大部分使用驼峰式命名法
- 大部分标签属性名,要采用驼峰写法
- 自定义属性不需要采用驼峰,保留
data-xx写法
function App(){
return (
<img
src='/vite.svg'
alt='vite log'
onClick={()=>alert('我被点了')}
data-size="30" />
)
}
export default App
渲染后的真实DOM
<img src="/vite.svg" alt="vite log" data-size="30">
# 4、特殊属性名处理
- 在JSX中 class 和 for 是关键字,所以class要用 className 代替,for 要用 htmlFor 代替
function App(){
return (
<div className="box" >
<label htmlFor="username">用户名</label>
<input type='text' id='username' />
</div>
)
}
export default App
渲染后的真实DOM
<div class="box">
<label for="username">用户名</label>
<input type="text" id="username">
</div>
# 6 、大括号内书写JS表达式
- 在JSX中,可以通过在
{}内来书写单一的 JS 表达式,也就是一段能够被求值的 JavaScript 代码。 - 判断是否为单一JS表达式,最简单的方法是,是否可以合法地写在
return后面
注意:在 JSX 的
{}大括号中,直接放一个对象 或函数,会报错,报错
function App() {
const text = "hello React";
const fn = () => 10;
const a = 1;
const b = 2;
const obj = {
attr: "对象属性值",
};
return (
<div>
{/* text是一个变量 */}
<p> {text}</p>
{/* fn() 是函数调用 */}
<p>{fn()}</p>
{/* a+b 数学运算 */}
<p>{a + b}</p>
{/* 三元表达式 */}
<p>{a > b ? "YES" : "NO"}</p>
{/* 数组操作 */}
<p>{[1, 2, 3].join(",")}</p>
{/* jsx也是一个表达式 */}
<div>{<p>{obj.attr}</p>}</div>
</div>
);
}
export default App;
以上代码渲染后的DOM
<div>
<p> hello React</p>
<p>10</p>
<p>3</p>
<p>NO</p>
<p>1,2,3</p>
<div>
<p>对象属性值</p>
</div>
</div>
以下写法,不是表达式,是属于JS语句,不能出现在{ }中
if(){}
function
for(var i=0;i<5;i++){}
switch(){ }
# 7、JSX 注释
JSX 中注释要写在 { } 大括号中
function App(){
return (
<div className="box" >
{/* htmlfor 等同于 for 属性 */}
<label htmlFor="username">用户名</label>
<input type='text' id='username' />
</div>
)
}
export default App
# 8、哪些值不会被渲染到页面
- 在 JSX 的
{}大括号中,true、false、undefined、null4个值是不会被渲染到页面上。 - 对于以上不渲染的值想进行输出,可将其与
''空字符串拼接或JSON.stringify()成字符串输出
const App=()=>{
return (
<>
<div>{true}</div>
<div>{false}</div>
<div>{undefined}</div>
<div>{null}</div>
<hr/>
<div>{true+''}</div>
<div>{false+''}</div>
<div>{undefined+''}</div>
<div>{null+''}</div>
</>
)
}
export default App
最终渲染结果如下:
<div id="root">
<div></div>
<div></div>
<div></div>
<div></div>
<hr>
<div>true</div>
<div>false</div>
<div>undefined</div>
<div>null</div>
</div>
# 9、JSX 的哪些地方可以使用大括号
在 JSX 中,只能在以下两种场景中使用大括号
在 JSX 标签体中,用来渲染标签体内容
在 JSX 标签属性
=符后面,用来渲染标签属性值
function App(){
const href="https://www.icodingedu.com"
const title="艾编程"
return (
// 标签属性 = 号符后面
<a href={href} title={title}>
{/* 标签体中 */}
{title}
</a>
)
}
export default App
# 10、变量中保存 JSX
function App(){
const value="请输入用户名"
{ /* myInput 中保存的是一段 JSX */ }
const myInput=<input type='text' value={value}/>
return (
<div>
{myInput}
</div>
)
}
export default App
最终渲染后DOM
<div>
<input type="text" value="请输入用户名">
</div>
# 11、特殊的 dangerouslySetInnerHTML 属性
dangerouslySetInnerHTML 是 React 提供的一个特殊属性,用于将字符串中的 HTML 内容解析为真实的 DOM 元素。
# 为什么需要它
React 默认会对所有动态内容进行转义,防止 XSS 攻击。所以字符串中的 HTML 内容不会被解析为真实 DOM 元素,而是显示为纯文本。
const userInput = '<strong>加粗文本</strong>';
// userInput 转义后的 <strong>加粗文本</strong>
<div>{userInput}</div>
渲染到页面效果

当你需要将字符串中的 HTML 内容解析为真实 DOM 元素时,就必须使用 dangerouslySetInnerHTML 属性。
const userInput = '<strong>加粗文本</strong>';
<div dangerouslySetInnerHTML={{ __html: userInput }}></div>

重要提示:为了防止 XSS 攻击,你在编写代码时,一定要确保传入给
dangerouslySetInnerHTML的内容一定是安全的。
# 什么是 XSS ?
XSS(Cross-Site Scripting,跨站脚本攻击) 是一种安全漏洞,攻击者通过在目标网站中注入恶意脚本,当其他用户访问该网站时,这些脚本会在用户浏览器中执行。
简单理解:攻击者让别人的网站执行了自己写的恶意代码。
示例:在评论中输入恶意内容
export default function App() {
// 假设这里的内容是用户在评论区输入框中输入的内容
const imgstr = `<img src="/favicon.svg" onClick="alert('cookie被偷了${document.cookie}')" />`;
return (
<>
<textarea value={imgstr}></textarea>
{/* 显示用户输入的评论 */}
<div dangerouslySetInnerHTML={{ __html: imgstr }}></div>
</>
);
}
上面示例中,当用户点击图标时,就可以获取到用户的 cookie 。
如果恶意者在图片的点击事件中,通过请求将你的 cookie 发送到他的服务器上,他就拿到了你的 cookie 身份。
# 12、HTML 转换为 JSX
如果你是老项目进行改造,需要将原来大量的 HTML 代码转换为 JSX,可以借助JSX在线转换器 (opens new window)
你可以通过点击放大图片来查看

转换前的HTML代码
<!--这里的注释会被转换-->
<h3>HTML 转换成 JSX </h3>
<div>这里有多个元素,所以转换后会被添加一个父标签<> 包裹</div>
<!--自定义属性第二个单词不用首字母大写-->
<img src="" data-Size="100px">
转换后的JSX代码
- 将所有
html包裹在<> <>中 - 注释做了转换
- img 标签后面加了
/,实现标签闭合
<>
{/*这里的注释会被转换*/}
<h3>HTML 转换成 JSX </h3>
<div>这里有多个元素,所以转换后会被添加一个父标签<> 包裹</div>
{/*自定义属性第二个单词不用首字母大写*/}
<img src="" data-size="100px" />
</>
# 四、条件渲染
通常我们需要根据不同的情况显示不同的内容。在React中,你可以通过使用 JavaScript 的 if 语句、&& 和 ?: 三目运算符来选择性地渲染 JSX
# 1、if 条件渲染
根据 if 的判断条件,组件返回不同的 JSX
const App=()=>{
const isShow=false
if(isShow){
return <div>条件为 true 渲染的内容</div>
}else{
return <div>条件为 false 渲染的内容</div>;
}
}
export default App
根据if的判断条件,给变量赋不同的JSX内容
const App=()=>{
// 用于判断的条件 0 未开始 1 正在进行中 2 已完成
let isDone=1;
// 保存渲染的JSX
let item;
if(isDone===0){
item=<div>未开始</div>
}else if(isDone===1){
item=<div>正在进行中..</div>
}else{
item=<div>已完成</div>
}
// 组件返回值
return item
}
export default App
# 2、switch条件渲染(用得少)
根据switch的判断条件,返回不同的 JSX 作为变量值
const App=()=>{
// 用于判断的条件 0 未开始 1 正在进行中 2 已完成
let isDone=1;
// 保存渲染的JSX
let item;
switch(isDone){
case 0:
item=<div>未开始</div>
break
case 1:
item=<div>正在进行中..</div>
break
case 2:
item=<div>已完成</div>
break
}
// 组件返回值
return item
}
export default App
# 3、&& 运算符
当条件满足时返回 JSX,不满足时不返回任何内容,可以用&&运算符
const App=()=>{
const isShow=false
return (
<div>
{ isShow && <div>为真显示</div> }
</div>
)
}
export default App
# 4、?: 三目运算符
当条件成立与不成立时,分别显示不同的JSX内容
const App=()=>{
const isShow=false
return (
<div>
{ isShow? <div>为真显示</div>:<div>为假显示</div> }
</div>
)
}
export default App
你还可以这样实现
const App=()=>{
const isShow=false
return (
<div>
<div>{isShow?"为真显示":"为假显示"}</div>
</div>
)
}
export default App
# 五、列表渲染
本小节我们将学习在 React 中,如何将一个数组渲染成一个列表。
你将会学习到
- JSX 中渲染数组的默认行为
- for 或 while 循环将数组渲染成列表
- 用数组的 map 方法渲染列表(推荐)
- 列表筛选(推荐)
- 用 key 保持列表项的顺序
# 1、JSX 中渲染数组的默认行为
JSX 中渲染数组时,React 是直接遍历数组,把每个元素当作 React 节点 去渲染
❌ 错误理解:很多人认为 React 渲染数组时,默认调的是
join("")
const App = () => {
const text = ["arry", "晓冉", "清心"];
// 变量中可以直接放 jsx
const list = [<li>arry</li>, <li>晓冉</li>, <li>清心</li>];
return (
<div>
<p>{text}</p>
<ul>{list}</ul>
</div>
);
};
export default App
渲染后的DOM
<div>
<p>arry晓冉清心</p>
<ul>
<li>arry</li>
<li>晓冉</li>
<li>清心</li>
</ul>
</div>
# 2、for 或 while 循环将数组渲染成列表(不推荐)
利用 for 或 while 循环,将数组中的每一项转换成一个JSX,添加到一个新数组,再渲染这个新数组
const App=()=>{
const list=["arry","晓冉","清心"]
const listItems=[];
// for循环遍历后,新数组listItems的值 [<li>arry</li>,<li>晓冉</li>,<li>清心</li>]
for(let i=0;i<list.length;i++){
listItems.push(<li>{list[i]}</li>)
}
// 在 jsx 中渲染新数组 listItems 时,会把数组中每个元素当作 React 节点去渲染
return <ul>{listItems}</ul>
}
export default App
渲染后的DOM
<ul>
<li>arry</li>
<li>晓冉</li>
<li>清心</li>
</ul>
# 3、map 方法将数组渲染成列表(推荐)
数组的 map 方法将返回一个新数组,新数组中的每个成员是由原数组中每个成员转换后的 JSX
const App=()=>{
const list=["arry","晓冉","清心"]
/* map方法返回的新数组: [<li>arry</li>,<li>晓冉</li>,<li>清心</li>]
然后 React 将遍历数组,将每一项当作 React 节点渲染 */
const newList=list.map((item,index)=><li>{item}</li>)
return <ul>{newList}</ul>
}
export default App
或以下写法
const App = () => {
const list = ["arry", "晓冉", "清心"];
return (
<ul>
{/* 换行时,要用 () 包裹返回值 */}
{list.map((item, index) => (
<li>{item}</li>
))}
</ul>
);
};
export default App;
渲染后的DOM
<ul>
<li>arry</li>
<li>晓冉</li>
<li>清心</li>
</ul>
# 4、列表筛选
如果我们只想将数组中的部分符合条件的数据渲染成列表,我们可以分以下两步来实现
- 先调用数组的
filter方法,筛选出所需的数组项 - 再用筛选过后的数组调用
map方法,将数组渲染成列表
示例:筛选出价格大于 10 块的水果
const App=()=>{
const fruits=[
{
id:1,
name:'蓝莓',
price:10
},
{
id:2,
name:'苹果',
price:5
},
{
id:3,
name:'芒果',
price:7
},
{
id:4,
name:'草莓',
price:15
},
{
id:5,
name:'香蕉',
price:4
}
]
// 筛选出价格大于10元的水果
const filterFruits= fruits.filter(item=>item.price>=10);
// 返回一个新数组,数组中每一项为一个JSX
const fruitItems=filterFruits.map(item=><li>{item.name}-{item.price}</li>)
// 渲染新数组 fruitItems
return <ul>{fruitItems}</ul>
}
export default App
你还可以向下面这样连着写
const App=()=>{
// ......省略部分代码
// 组件返回值
return <ul>{
fruits.filter(item=>item.price>=10)
.map(item=><li>{item.name}-{item.price}</li>)
}
</ul>
}
export default App
# 5、用 key 保持列表项的顺序
在 React 中,如果你按下面方式渲染列表(不在 li 上添加 key 属性),React 会警告
const App = () => {
const fruit = [
{ id: 1, text: "苹果" },
{ id: 2, text: "香蕉" },
{ id: 3, text: "橙子" },
];
return (
<ul>
{fruit.map((item) => (
<li>{item.text}</li>
))}
</ul>
);
};
export default App;

这是因为你必须给数组中的每一项都指定一个 key——它可以是字符串或数字的形式,只要能唯一标识出各个数组项就行。
key 属性帮助 React 识别列表中的每一个元素,从而决定哪些节点可以复用、哪些需要更新或移动,避免状态错位和性能问题。
key 属性只是用来标识列表项的唯一性,不会出现在最终的 HTML 结构里。
代码示例:列表添加 key 属性
<ul>
{fruit.map((item) => (
{/* 给列表项 添加唯 一的 key 后,控制台就不报错了 */}
<li key={item.id}>{item.text}</li>
))}
</ul>
# key 需要满足的条件
- key 值在兄弟节点之间必须是唯一的。 不过不要求全局唯一,在不同的数组中可以使用相同的 key。
- key 值不能改变,否则就失去了使用 key 的意义!所以千万不要在渲染时动态地生成 key。
- 数组索引 不要作为
key属性值。- 如果后续不会对数组数据做任何操作,就不会有影响。
- 如果后期对数组做 增 ,删 操作时,就会改变每项的索引,key 值就改变,并不能保证唯一。
// ❌ 不推写法 : 利用数组索引作为 key 属性值
<ul>
{fruit.map((item, index) => (
<li key={index}>{item.text}</li>
))}
</ul>
温馨提示
- 为什么 key 在兄弟节点之间要唯一,且不能改变 ?
针对上面1个疑问,我们将在 如何设置列表 key 值 这里会详细讲解。
# 六、事件绑定
本小节我们将学习,如何在 JSX 中给原生的 HTML 标签添加 DOM 事件。
你将会学习到:
- 如何给 HTML 标签添加DOM事件
- 事件处理函数如何传参
- 事件处理函数中如何获取event事件对象
- 事件传播
# 1、如何给 HTML 标签添加 DOM 事件
给 HTML 标签添加 DOM 事件时,需要:
- 使用以
on开头、后跟事件名的属性,事件名每个单词首字母大写 例如:onClick、onMouseOver、onKeyDown - 属性值是一个事件处理函数(推荐函数名以
handle开头,后跟事件名称,便于识别)
const App=()=>{
/* handleClick 为事件处理函数 */
const handleClick=()=>{
alert('我被点击了')
}
/* 为div元素添加了Click点击事件,并添加事件处理函数handleClick,当元素被点击时,就会调用handleClick函数 */
return <div onClick={handleClick}>点我呀!</div>
}
export default App
重要注意事项:
事件处理函数不要加括号:
onClick={handleClick} ✅
onClick={handleClick()} ❌(会在渲染时立即执行,而非等待点击)
# 捕获阶段触发事件
DOM 事件流程分为三个阶段:
- 捕获阶段:从根节点向下传递到目标元素
- 目标阶段:事件到达目标元素
- 冒泡阶段:从目标元素向上冒泡回根节点
默认情况下,onClick 等事件是在冒泡阶段触发。
如果需要在捕获阶段触发,可以使用 on + 事件名(首字母大写) + Capture 的格式。
const App = () => {
const handleClickCapture = () => {
alert('捕获阶段触发:我被点击了');
};
return <div onClickCapture={handleClickCapture}>点我呀(捕获阶段)</div>;
};
# 常见事件对照表
更多事件绑定查阅 React 官网:https://zh-hans.react.dev/reference/react-dom/components/common
| 冒泡阶段(默认) | 捕获阶段 |
|---|---|
onClick | onClickCapture |
onMouseOver | onMouseOverCapture |
onKeyDown | onKeyDownCapture |
onFocus | onFocusCapture |
onBlur | onBlurCapture |
# 2、事件处理函数如何传参
如果我们给事件处理函数传递参数,推荐采用以下利用箭头函数写法
const App=()=>{
const username="清心"
// ts写法,参数加了类型标注
const handleClick=(username)=>{
alert(`${username},你好呀!!`)
}
// 箭头函数返回元素被点击后要调用的handleClick函数
return <div onClick={()=>handleClick(username)}>
点击我
</div>
}
export default App
# 3、事件处理函数中获取 event 事件对象
默认情况下,事件处理函数接受一个事件对象作为唯一的参数。它通常被称为 e (代表 “event” 事件)。你可以使用此对象来读取有关事件的信息。
注意:
event并不是原生的事件对象,而是 React 合成事件对象,它是对原生事件的跨浏览器封装,具有与原生事件相同的接口。
- 事件处理函数不传参情况,则事件处理函数的第一个参数默认为 event
const App = () => {
// e为事件对象 添加了ts类型标注
const handleClick = (e) => {
// e.target 获取被点击的元素:<div>点击我 </div>
console.log(e.target);
alert(`清心,你好呀!!`);
};
// 添加事件
return <div onClick={handleClick}> 点击我 </div>;
};
export default App;
- 如果需要向事件处理函数传递额外参数,不能直接写
onClick={handleClick(username)}(这样会在渲染时立即执行),而是要通过箭头函数将事件对象e作为第一个参数显式传递。如:(e) => handler(e, 额外参数)
const App=()=>{
const username="清心"
// e为事件对象 添加了ts类型标注
const handleClick=(e,username)=>{
// e.target获取被点击的元素 : <div>点击我 </div>
console.log(e.target)
alert(`${username},你好呀!!`)
}
// 事件处理函数的第一个参数默认为事件对象
return <div onClick={(e)=>handleClick(e,username)}>点击我 </div>
}
export default App
# 4、事件传播
在 React 中所有原生事件都会传播,除了 onScroll。在某些情况下,我们可能需要阻止事件冒泡或阻止事件的默认行为。
# 4.1、阻止事件冒泡
e.stopPropagation() // e为事件对象
以下代码中,我们在 span 元素的事件处理函数中添加了e.stopPropagation()阻止了事件冒泡。所以当我们点击 h3 元素时,并不会触发外层 div 元素上的点击事件。
const App = () => {
const handleClick1 = (e) => {
// 绑定事件元素的标签名
alert(e.currentTarget.tagName);
};
const handleClick2 = (e) => {
// 阻止事件冒泡
e.stopPropagation();
// 触发事件元素的标签名
alert(e.target.tagName);
};
return (
<div onClick={handleClick1}>
div标签
<h3 onClick={handleClick2}>h3标签</h3>
</div>
);
};
export default App;

如果将 e.stopPropagation() 代码注释掉,你会发现,点击 h3 元素时,h3 元素和 div 元素绑定的事件处理函数都会被调用。

# 4.2、阻止事件的默认行为
e.preventDefault() // e为事件对象
以下代码中,我们添加了e.preventDefault()代码,阻止了 a 标签点击默认会跳转到新页面的行为。所以点击 a 标签时,并不会发生页面的跳转。
const App = () => {
const handleClick = (e) => {
e.preventDefault();
};
return (
<a href="http://www.icodingedu.com" onClick={handleClick}>
点击无法跳转
</a>
);
};
export default App;
# 七、样式绑定
本小节我们学习在 React 中,如何给元素添加样式
你将会学习到:
- 如何添加行内样式
- 如何添加全局样式
- 如何添加局部样式 -CSS 模块化
- sass 预处理器
# 1、行内样式
JSX 中标签的
style属性可以为元素添加行内样式。行内样式值书写为一个对象,对象的属性名为 css 属性名,但属性名必须采用驼峰命名
function App() {
/* 通过变量定义css样式 */
const styleObj = {
fontSize: "20px",
backgroundColor: "skyblue",
margin: 0,
};
return (
<div>
<h3 style={styleObj}>h3 标题内容</h3>
<p
style={{
color: "red",
}}
>
p 标签描述内容
</p>
</div>
);
}
export default App;
渲染后DOM如下:
<div>
<h3 style="font-size: 20px; background-color: skyblue; margin: 0px;">h3 标题内容</h3>
<p style="color: red;"> p 标签描述内容 </p>
</div>
# 2、全局样式
通过 import './xx.css' 导入的 CSS 样式默认是全局样式,它会作用于整个项目。
import "./App.css"
const App=()=>{
// box类名在App.css文件中有声明
return <div className="box"></div>
}
export default App
App.css文件内容如下
.box {
width: 100px;
height: 100px;
background-color: skyblue;
}
渲染后效果如下:

# 3、局部样式-CSS模块化
如果希望某些 CSS 样式只作用于当前组件,而不影响其他组件,可以采用 CSS 模块化的方式来引入样式。在使用 Vite 构建项目时,对于以 .module.css 结尾的文件,Vite 会将其作为 CSS 模块进行处理,从而实现样式的局部作用域。
Vite 官方教程:CSS 模块化 (opens new window)
使用 CSS 模块化的具体步骤
- 第一步:在
src目录下新建App.module.css文件,并编写样式
.box {
width: 100px;
height: 100px;
background-color: skyblue;
}
- 第二步:在组件文件中使用
import导入该 CSS 模块
import Styles from './App.module.css'
- 第三步:通过
Styles.类名的方式获取对应的 CSS 类名,并赋值给className
<!-- 填加单个classname-->
<div className={Styles.box}></div>
完整代码示例
import Styles from './App.module.css'
const App=()=>{
return <div className={Styles.box}></div>
}
export default App
最终渲染效果如下:

通过模块化方式引入的 CSS 类名,最终会被加上一段 hash 值,这样就能达到唯一性。不会影响到其它组件的 css 样式。
# 3.1、模块化 CSS 中声明全局样式
如果你想在.module.css 文件中定义全局 CSS 样式,可以借助 :global 伪类来实现。
- App.module.css 文件
/* 局部样式 */
.box {
width: 100px;
height: 100px;
background-color: skyblue;
}
/* 全局样式 */
:global(.font-color-red) {
color: red;
}
- App.tsx 文件
import Styles from './App.module.css'
const App=()=>{
return <div className={Styles.box}>
<div className='font-color-red'>全局样式控制字体变红</div>
</div>
}
export default App
最终渲染后的效果如下:

# 3.2、CSS 模块化中: CSS 类名编写规则
当我们以模块化方式加 CSS 时,CSS的类名最好以驼峰命名,否则在调用时就必须以方括号形式调用。
App.module.css
/* 必须以 Styles['my-box'] 方式调用 */
.my-box {
width: 100px;
height: 100px;
background-color: skyblue;
}
/* 可以以 Styles.myTitle方式调用 推荐驼峰命名,因为使用方便 */
.myTitle {
background-color: khaki;
margin: 0;
text-align: center;
}
代码示例
import Styles from './App.module.css'
const App=()=>{
// 不能以Styles.myBox和Styles.my-box形式调用,只能Styles['my-box']
return <div className={Styles['my-box']}>
<p className={Styles.myTitle}>标题</p>
</div>
}
export default App
# 3.3、CSS 模块化配置
如果想在使用 CSS 模块,css 样式以xxx-aaa形式书写,而在调用时可以Styles.xxxAaa形式调用,可以在vite.config.js文件中添加如下配置。
更多模块化配置,参考 Vite 官网的 css.module (opens new window)
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
css: {
modules: {
// 如何转换CSS类,'camelCase'意味着所有CSS类名都将被转换为驼峰命名法(camelCase)。例如.my-box转换为myBox
localsConvention: 'camelCase',
},
},
});
添加了以上配置后,我们在JSX中就可以通过Styles.myBox来应用App.module.css中声明的.my-box样式
import Styles from './App.module.css'
const App=()=>{
// 在Vite.config.js中添加上面提到的配置后,就可以通过Styles.myBox形式调用
return <div className={Styles.myBox}>
<p className={Styles.myTitle}>标题</p>
</div>
}
export default App
# 4、CSS 预处理器 Sass
Vite 官方教程:CSS 预处理器 (opens new window)
如果我们想在 React 中使用 Sass 来编写 CSS,需要按以下步骤来操作
第一步:安装 sass
sass为纯 JS版本,编译大型项目时相对较慢。sass-embedded为嵌入式版,在处理大型、复杂的 Sass 文件和频繁编译时更快。
npm i -D sass-embedded # 安装sass
# 或
npm i -D sass # 安装 sass
安装好后,可执行 npm list sass 查看当前项目安装的 sass 版本。
- 第二步:创建
xxx.scss或xxx.module.scss文件,可以以 sass 语法编写 css
// App.scss 与 App.module.scss 文件内容相内
.my-box{
width:100px;
height:100px;
background-color:skyblue;
.my-title{
font-size:16px;
background-color:khaki;
}
}
- 第三步:使用 import 关键词导入
.css,然后在 JSX 中使用对应的 css 类名
import './App.scss'
const App=()=>{
return <div className='my-box'>
<div className='my-title'>标题</div>
</div>
}
export default App
渲染后效果如下:

如果导入的是.module.scss文件,则以模块化方式调用类名
import Styles from './App.module.scss'
const App=()=>{
return <div className={Styles.myBox}>
<div className={Styles.myTitle}>标题</div>
</div>
}
export default App
渲染后效果如下:

# 八、动态操作元素 class 类名
动态操作元素 class 类名需要与 state 状态一起配合完成,所以这块内容,放在学完 useState 组件状态之后讲。
点击学习:动态操作元素 class 类名
大厂最新技术学习分享群
微信扫一扫进群,获取资料
X